#ifndef SLOTGUTS_H
#define SLOTGUTS_H


#pragma warning( disable: 4786 )
#include <list>
#include <vector>


#include "lib.h"
#include "slotspin.h"
#include "subgamedata.h"
#include "tracksql.h"

#ifdef DEBUGSPIN
typedef vector<uint8> ReelDebugStops;
typedef vector<ReelDebugStops> DebugStops;
#endif


typedef struct _info
{
	String			login;
    PID				pid;
    SESSIONID		sessionId;
    uint8			machineID;
    uint16          machineSubID;
    uint16          creditValue;
    bool8			forMoney;
    int32           compFactor;
    int32           balance;
} Info, *pInfo;

class SlotGameDenomination
{
public:

    SlotGameDenomination(uint16 machineSubID, uint16 creditValue, bool8 isDefault)
    {
        m_machineSubID = machineSubID;
        m_creditValue = creditValue;
        m_isDefault = isDefault;
    }
 
    uint16          m_machineSubID;
    uint16          m_creditValue;
    bool8           m_isDefault;
};

/**
 * @brief a class defining the slot reel icon.
 * 
 */
struct SlotIcon
{
    public:
        /**
         * @brief The reel for this icon.
         */
        uint8    reelid;

        /**
         * @brief The position on the reel.
         */
        uint8    position;

        /**
         * @brief The id of the icon it represents.
         */
        uint8    iconid;

        /**
         * @brief The probability weighting
         */
        int64   weight;

        /**
         * @brief the name as a string.
         */
        char *iconName;

};


/**
 * @brief a class defining the slot payline position.
 * 
 */
struct SlotPaylinePosition
{
    public:
        /**
         * @brief The payline index.
         */
        uint8    payline;

        /**
         * @brief The reel for this payline position.
         */
        uint8    reelid;

        /**
         * @brief The offset position.
         */
        uint8    wheelposition;

};

/**
 * @brief a class defining the slot payout line.
 */
struct SlotPayout
{
    public:
        /**
         * @brief The index of this payline.
         */
        uint8    payoutTypeId;

        /**
         * @brief The bet amount associated with this payline.
         */
        uint8    betAmount;

        /**
         * @brief The string name of this payout.
         */
        char *payoutName;

        /**
         * @brief The amount this line pays in cents.
         */
        int32 payoutAmount;
};



#ifdef DEBUGSPIN
class DebugStopFuncBase;
typedef std::map< int32, DebugStopFuncBase* > DebugFunctionMap;
#endif



/**
 * @brief base class for all slots guts.
 * 
 * This is the base class for all slots guts.  It is capable
 * of loading an instance of itself and instantiating a complete
 * guts from the database.
 * 
 */
class SlotGuts
{
	public:

        /**
         * @brief The slot payouts list type.
         */
        typedef vector<const SlotPayout *> SlotPayColumn;

        /**
         * @brief The slot reels list type.
         */
        typedef vector<const SlotIcon *> SlotReel;

        /**
         * @brief Public Constants used by this class.
         */
        enum PUB_CONSTANTS
        {
            /**
             * @brief the max icon name length.
             */
            ICON_NAME_LEN = 64,
        };

        /**
         * @brief Create an instance of a slot guts.
         * @param machid the machine id.
         * @return a slot guts pointer.
         */
        static SlotGuts *createGuts(uint8 machid);

        /**
         * @brief Base destructor.
         * 
         * @param   gutsid the guts identifier.
         */
		virtual ~SlotGuts();

        /**
         * @brief Check the bet for validity.
         * @param bet The bet in credits.
         * @return true if the bet is valid.
         */
		virtual bool8 isValidBet(uint8 bet);

        /**
         * @brief Get the game number these spins refer to.
         * @return The game number or 0 if there is no meta game.
         */
        int32 getReferenceGameNum();

        /**
         * @brief Check the bet for validity.
         * @return The numb
         */
		virtual void *getCustomData();

        /**
         * @brief create a spin then complete it.
         * @param bet the bet in cents.
         * @param multibankBet the bet in cents.
         * @param lines a pointer to the array of lines bet.
         * @param lineCount the number of lines bet.
         * @param bonusGame the bonus game identifier.
         */
		virtual SlotSpin *spin(uint8 bet, int32 multibankBeCentst, uint8 *lines, uint8 lineCount, uint8 bonusGame);

        /**
         * @brief create a spin then complete it.
         * @param bet the bet in cents.
         * @param lines a pointer to the array of lines bet.
         * @param lineCount the number of lines bet.
         * @param bonusGame the bonus game identifier.
         */
		virtual SlotSpin *spin(uint8 bet, uint8 *lines, uint8 lineCount, uint8 bonusGame);

        /**
         * @brief Iterate the lines played and determine winners.
         * @param spin the slot spin.
         * @see classifyStops
         */
        virtual void classifyResult(SlotSpin *spin);

        /**
         * @brief See if a particular icon configuration is a winner.
         * @param stops the array of icons.
		 * @param bet the amount of the bet
		 * @param line the line for this stop - useful for multi-line slots
         */
        virtual uint8 classifyStops(uint8 *stops, uint8 bet, uint8 line);

        /**
         * @brief See if a spin has scatter winners
         * @param spin the slot spin
         */
        virtual void classifyScatters(SlotSpin *spin){};

        /**
         * @brief Get the guts id.
         * @return The id for this guts.
         */
		int32 getGutsId();

        /**
         * @brief Get the guts name.
         * @return The name for this gut.
         */
		void getGutsName(char* name);


        /**
         * @brief Set the guts id.
         * @param gutsId The id for this guts.
         */
		void setGutsId(int32 gutsId);

        /**
         * @brief Get the player id.
         * @return the player id.
         */
		const PID& getPid();

        /**
         * @brief Get the jackpot id.
         * @return The id for this jackpot.
         */
		int32 getJackpotId();

        /**
         * @brief Set the jackpot id.
         * @param jackpotId The id for this jackpot.
         */
		void setJackpotId(int32 jackpotId);

        /**
         * @brief Check the for money flag.
         * @returns true if for money.
         */
        bool8 isForMoney();

		bool8 shouldSaveGameData();

        /**
         * @brief Check the progressive flag.
         * @returns true if progressive is on.
         */
        bool8 isProgressive();

        /**
         * @brief Set the progressive flag.
         * @param progressive the new flag setting.
         */
        void setProgressive(bool8 progressive);

        /**
         * @brief Set the state of the machine to bonus game.
         * 
         * Set the state of the machine to bonus game.  For the base class
         * this method does nothing.
         */
		virtual void playBonusGame(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData);

        /**
         * @brief Get the number of reels
         * @returns The number of reels for this guts.
         */
        uint8 getReelCount();

        /**
         * @brief Get the size of the reel.
         * @param reelid the reel number.
         * @returns The number of icons for this reel.
         */
        uint8 getIconCount(uint8 reelid);

        /**
         * @brief Get the icons
         * @param reel the reel number
         * @param icons an array of SlotIcons (allocated by caller).
         * Get the icons for a specific reel.
         */
        const SlotReel *getSlotReel(uint8 reelid);

        /**
         * @brief Reset the reels.
         */
        void clearWheelhouse();

        /**
         * @brief add an icon.
         * @param reelid the reel number.
         * @param iconid the id of the icon.
         * @param reelposition the location of the icon on the reel.
         * @param weight the number needed to acheive this location on the reel.
         * @param iconName the null terminated string description of the icon.
         */
        void addIcon(uint8 reelid, uint8 iconid, uint8 reelposition, int64 weight, const char *iconName);

        /**
         * @brief Get the number of bets
         * @returns The number of bets allowed.
         */
        uint8 getBetCount();


        /**
         * @brief Get the number of payouts.
         * @param bet the bet column.
         * @returns The number of payouts for that bet.
         */
        uint8 getPayoutCount(uint8 bet);

        /**
         * @brief Get the payouts in a bet column.
         * @param bet the bet amount in coins.
         */
        const SlotPayColumn *getPayColumn(uint8 bet);

        /**
         * @brief Reset the payouts.
         */
        void clearPayTable();

        /**
         * @brief add a payout.
         * @param payoutTypeId the index of the payout.
         * @param betAmount the bet in coins.
         * @param payoutName the null terminated string name for this payout.
         * @param payoutAmount the amount of the payout in credits.
         */
        void addPayout(uint8 payoutTypeId, int8 betAmount, const char *payoutName, int32 payoutAmount);


        /**
         * @brief Reset the paylines.
         */
        void clearPaylines();

        /**
         * @brief get the number of paylines.
         * @return the number of paylines.
         */
        uint8 getPaylineCount();

        /**
         * @brief get the position of the specified payline.
         * @param paylineid the payline index.
         * @param reelid the reel index
         * @return the payline position.
         */
        uint8 getPaylinePosition(uint8 paylineid, uint8 reelid);

        /**
         * @brief add a payline.
         * @param paylineid the payline index.
         * @param reelid the reel index.
         * @param wheelposition the reel offset.
         */
        void addPayline(uint8 paylineid, uint8 reelid, uint8 wheelposition);


        /**
         * @brief Get the machine id.
         * @returns the machine id.
         */
        uint8 getMachId();

        /**
         * @brief Get the subgame data.
         * @returns a read-only pointer to the subgame data array.
         */
        SubGame *getSubGame();

        /**
         * @brief get the subgame data size.
         * @return the subgame data size.
         */
		int32 getSubGameSize();

        /**
         * @brief Get the top payline
         * @return the maximum payout.
         */
        const SlotPayout *getMaxPayout();

        /**
         * @brief Get the specified payline
         * @param bet the bet in credits.
         * @param payouttypeid the index of the payline.
         * @return the payout.
         */
        const SlotPayout *getPayout(uint8 bet, uint8 payouttypeid);

        /**
         * @brief Get the specified icon
         * @param reelid the reel number.
         * @param position the reel position.
         * @return the icon.
         */
        const SlotIcon *getIcon(uint8 reelid, uint8 position);

        /**
         * @brief Load the guts
         * @param info the information pointer indicating the type of machine to load.
         * @return TRUE if the guts loaded properly
         */
		virtual bool8 load(Info *info);

        /**
         * @brief Save the subgame data to the database.
         */
		virtual void saveSubGameData();

        /**
         * @brief Load the subgame data from the database.
         */
		virtual void loadSubGameData();

        /**
         * @brief Save the subgame data to the database in an archive table (persistent).
		 * Make sure the current game has been saved.  Only then can you get a valid gamenum.
		 * SlotGuts::saveGame () must get called before gamenum is valid
         * @param gamenum the game number for this game.
         */
		virtual void archiveSubGameData(int32 gamenum);


        /**
         * @brief get the number of spins.
         * @return pointer to a SlotSpin.
         */
        uint8 getSlotSpinCount();

		virtual bool8 loadSavedGame();

        /**
         * @brief get a specific spin.
         * @param spinid the index of the desired spin.
         * @return pointer to a SlotSpin.
         */
        SlotSpin *getSlotSpin(uint8 spinid);

        /**
         * @brief complete the already created spin.
         * @param gamenum the reference game number.
         * @param linenumber the reference line number.
         * @param bonusgamenum the bonus game id. 
         * @param bonusgametype the bonus game type. 
         * @param playerchoice the choice made for this bonus game by the player. 
         * @param winresult the cents won for this bonus game entry.
         */
		virtual bool8 createBonusGame(int32 gamenum, uint8 linenumber, uint8 bonusgamenum, uint8 bonusgametype, uint8 playerchoice, int32 winresult);

        /**
         *  Has the game been saved to the database?
         *  @return true if the game has been saved.
         */
		virtual bool8 isGameComplete();

        /**
         * @brief Save the bonus game information.
         * @return true if the save succeeds.
         */
		virtual bool8 saveBonusGame();

        /**
         * @brief Save the reel and payout information for each spin.
        * @return true if the save succeeds.
         */
		bool8 saveSpins();

        /**
         * @brief Save each spin.
        * @return true if the save succeeds.
         */
		virtual bool8 saveGame();

        /**
         * @brief complete each created slot spin.
         * @return True if the game completion succeeds.
         */
		bool8 completeGame();

        /**
         * @brief Get the info pointer.
         * @return the constant info pointer.
         */
        Info *getInfo();


        /**
         * @brief Check for a malfunction
         * @returns true if the machine has malfunctioned.
         */
        virtual bool8 isMalfunction();

        /**
         * @brief Mutator method for the Fast Play Enabled property
         * @param bFastPlayEnabled The new value of the Fast Play enabled property.
         */
        void  setFastPlayEnabled( bool8 bFastPlayEnabled );

        /**
         * @brief Accessor method for the Fast Play Enabled property
         * @returns true Fast Play is enabled for this machine..
         */
        bool8 getFastPlayEnabled() const;

        /**
         * @brief Reset the guts for a new game.
         */
        virtual void resetForNewGame();

        /**
         * @brief Reset the guts for a new game.
         */
        void setCanResetForNewGame(bool8 bCanResetForNewGame);

        /**
         * @brief Reset the guts for a new game.
         */
        bool8 getCanResetForNewGame() const;

        uint8 getDenominationCount() const;

        void clearDenominations();
        
        SlotGameDenomination*  getDenomination(uint8 nIndex);

        void addDenomination(uint16 machineSubId, uint16 creditValue, bool8 isDefault);

#ifdef DEBUGSPIN
		bool8 isForcedJackpot(){return m_bForceRandomJackpot;};
#endif

	protected:

        /**
         *  Signal game complete
         *  @return game complete state
         */
		bool8 setGameComplete(bool8 isComplete);

        /**
         * @brief complete the already created spin.
         * @param spin the slot spin.
         */
		virtual void spin(SlotSpin *spin);

        /**
         * @brief make sure that the spin settings are valid
         * @param the spin that we're validating
         * kharmon.  08-17-05.  #4962.  #5116.
         */
		virtual bool isValidSpin(SlotSpin *spin);

        /**
         * @brief Create a guaranteed winning spin.
         * @param bet the bet in cents.
         * @param bonusGame the bonus game identifier.
         */
        SlotSpin *makeWinningSpin(uint8 bet, uint8 bonusGame);

        /**
         * @brief Create a guaranteed winning spin.
         * @param spin the spin pointer.
         */
        void makeWinningSpin(SlotSpin *spin);

        /**
         * @brief Iterate the lines and determine if something has occurred that
         *        alters the actual values on the reel.
         * @param spin the slot spin.
         */
        virtual void preSpinClassifyResults(SlotSpin *spin);
        
        /**
         * @brief Iterate the lines and determine if something has occurred that
         *        alters the actual values on the reel.
         * @param spin the slot spin.
         */
        virtual void postSpinClassifyResults(SlotSpin *spin);
        
        /**
         * @brief Whether this game uses saved games.
         */
        virtual bool8 hasSavedGames(){return false;};


        /**
         * @brief Create an instance of a slot
         */
		SlotGuts();

        /**
         * @brief The collection of payouts (Internal version allows cleanup). 
         */
        typedef vector<SlotPayout *> ISlotPayColumn;

        /**
         * @brief The collection of pay columns.
         */
        typedef vector<ISlotPayColumn *> SlotPayTable;

        /**
         * @brief The collection of icons (Internal version allows cleanup).
         */
        typedef vector<SlotIcon *> ISlotReel;

        /**
         * @brief The collection of slot reels.
         */
        typedef vector<ISlotReel *> SlotWheelHouse;


        /**
         * @brief The collection of payline positions (or offsets).
         */
        typedef vector<SlotPaylinePosition *> SlotPaylineReel;

        /**
         * @brief The collection of payline reels.
         */
        typedef vector<SlotPaylineReel *> SlotPayline;

        typedef vector<SlotGameDenomination*> SlotGameDenominationVector;

        /**
         * @brief A vector of paylines.  Paylines hold reels.  Reels hold offsets.
         */

        SlotPayline m_paylines;

        /**
         * @brief A vector of pay columns.
         */
        SlotPayTable m_payTable;

        /**
         * @brief A vector of reels.
         */
        SlotWheelHouse m_wheelHouse;

        SlotGameDenominationVector  m_vecDenominations;

        /**
         * @brief Check that the gutsid is valid.
         */
        virtual bool8 isValidGuts();

        /**
         * @brief Create an array of initialized subgame data.
         */
		virtual void allocateSubGameData();

        /**
         * @brief the machine id.
         */
        uint8 m_machid;

        /**
         * @brief Whether there is a subgame to load.
         */
        bool8 m_hasSavedGame;

        /**
         * @brief The subgame information.
         */
        SubGame *m_subGame;

        /**
         * @brief Malfunction short circuit.
         */
        bool8 m_hardMalfunction;

        /**
         * @brief Find the icon position.
         * @returns the position of the icon represented by this weight.
         */
        virtual uint8 findPositionByWeight(int64 weight, uint8 reelid);

		/**
         * @brief Get a random array element - numElements should be a power of two for true random results
         * @returns an item between data[0] and data[numElements-1]
         */		
		int32 getRandomFromArray(int32* data, uint32 numElements);

        /**
         * @brief The slot spins.
         * @returns the position of the icon represented by this weight.
         */
        vector <SlotSpin *>m_spins;

        /**
         * @brief Get the max aggregate.
         * @returns the value of the max aggregate
         */
        virtual int32 getMaxAggregate();

        /**
         * @brief Remove the spins from the list.
         */
        void clearSpins();

        /**
         * @brief the wheel position after applying the offset.
         * @param reelid the reel index.
         * @param position the original position on the reel.
         * @param offset the number of stops to look forward on the reel.
         */
        uint8 relativeWheelPosition(uint8 reelid, uint8 position, uint8 offset);

        /**
         * @brief get the reel offset position for the payline.
         * @param paylineid the payline index.
         * @param reelid the reel index.
         */
        SlotPaylinePosition *getPayline(uint8 paylineid, uint8 reelid);

        /**
         * @brief A pointer to the info structure.
         */
        Info *m_info;

        /**
         * @brief The id for this jackpot (if progressive).
         */
		int32 m_jackpotid;

        /**
         * @brief The progressives flag.
         */
		bool8 m_progressive;

        /**
         * @brief The game number all spins should be referning to.  Default is 0.
         */
        int32 m_referenceGameNum;

        bool8 m_bFastPlayEnabled;

		//Debug Spins Functionality
#ifdef DEBUGSPIN
		bool8 m_bDebugSpinsMode;
		bool8 m_bForceRandomJackpot;
		void DebugStops_Loser( SlotSpin *spin = NULL );		
		virtual void Debug_SetLoserStops( uint8* pStops );
		void DebugStops_RandomSpin( SlotSpin * spin);
		void DebugStops_RandomJackpot( SlotSpin *spin);
		void processDebugStops( SlotSpin *spin );
		DebugStops::size_type  m_nDebugSpinIndex;
		DebugStops m_debugReelStops;
		int32	m_debugSpinCount;	// Number of spins in the m_debugReelStops array

		DebugFunctionMap  m_mapDebugFunctions;

        virtual void initDebugFunctionMap();
        void    addDebugStopFunction( int32 key, DebugStopFuncBase* func );

        enum
        {
			THIS_IS_A_FUNCTION			= 255,

			//This is the range of GENERATOR ids implemented in the base SlotGuts class (1-100)
            BEGIN_BASE_FUNCTION_RANGE	= 1,			

			// Defined functions, generic for all games
			EVT_DEBUG_FUNCTION_LOSER_SPIN = BEGIN_BASE_FUNCTION_RANGE,
			EVT_DEBUG_FUNCTION_RANDOM_SPIN,
			EVT_DEBUG_FUNCTION_RANDOM_JACKPOT,
        };

#endif

    private:

        /**
         * @brief True if game is complete.
         */
		bool8 m_gameComplete;

        bool8 m_bCanResetForNewGame;

        /**
         * @brief Private Constants used by this class.
         */
        enum PRIV_CONSTANTS
        {
            /**
             * @brief the max bet.
             */
            MAX_BET = 3,

            /**
             * @brief ten thousand credits.
             */
            MAX_AGGREGATE = 10000,

        };


        /**
         * @brief The id for this guts.
         */
		int32 m_gutsid;
};

class DebugStopFuncBase
{
	protected:
    SlotGuts* m_guts;

	public:
    DebugStopFuncBase( SlotGuts* guts )
        : m_guts( guts )
    {
    }
    
    virtual void operator() ( SlotSpin *spin = NULL )
    {
    }
};

#ifdef DEBUGSPIN
template <class derived>
class DebugStopFunc : public DebugStopFuncBase
{
	public:
    typedef	void (derived::*DEBUG_STOP_PROC)( SlotSpin *spin );

	private:
    DEBUG_STOP_PROC m_proc;
    
	public:    
    DebugStopFunc()
        : DebugStopFuncBase( NULL ), m_proc( NULL )
    {
    }
    
    DebugStopFunc( SlotGuts* guts, DEBUG_STOP_PROC proc)
        : DebugStopFuncBase( guts ), m_proc( proc )
    {
    }
    
    void operator() ( SlotSpin *spin )
    {
        (((derived*)(m_guts))->*m_proc)( spin );
    }

    void registerStopFunction( derived* pThis, DEBUG_STOP_PROC proc )
    {
        m_guts = pThis;
        m_proc = proc;
    }
};

template <class derived, class Argtype>
class DebugStopFunc1Arg : public DebugStopFuncBase
{
	public:
    typedef	void (derived::*DEBUG_STOP_PROC)( Argtype a, SlotSpin *spin );

	private:
    DEBUG_STOP_PROC m_proc;
    Argtype         m_arg;
    
	public:    
    DebugStopFunc1Arg()
        : DebugStopFuncBase( NULL ), m_proc( NULL ), m_arg(NULL)
    {
    }
    
    DebugStopFunc1Arg( SlotGuts* guts, DEBUG_STOP_PROC proc, Argtype argval)
        : DebugStopFuncBase( guts ), m_proc( proc ), m_arg( argval )
    {
    }
    
    void operator() ( SlotSpin *spin )
    {
        (((derived*)(m_guts))->*m_proc)( m_arg, spin );
    }

    void registerStopFunction( derived* pThis, DEBUG_STOP_PROC proc, Argtype argval )
    {
        m_guts = pThis;
        m_proc = proc;
        m_arg = argval;
    }
};

#endif

#endif // SLOTGUTS_H
